#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <ctype.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/wait.h>
#include <assert.h>
#include <errno.h>

#define NUM 1024
#define OPT_NUM 64
#define NONE_REDIR   0
#define INPUT_REDIiR  1 //输入重定向
#define OUTPUT_REDIR 2  //输出重定向
//输出重定向是把原来往屏幕上输出的东西改到输出到其他文件
//输入重定向是把键盘输入的东西改为文件往里输入的东西
#define APPEND_REDIR  3

#define trimSpace(start) do{\
            while(isspace(*start)) ++start;\
        }while(0)    //注意这里不用加;因为这里是宏函数我们在使用的时候trimSpace();习惯加;所以在写宏函数的时候就不用加了不然替换的时候会导致多一个;

char line_command[NUM];
char* myargv[OPT_NUM];
int  lastCode = 0;
int  lastSig = 0;

int redirType = NONE_REDIR;//重定向类型
char *redirFile = NULL;//重定向文件
// "ls -a -l -i > myfile.txt" -> "ls -a -l -i" "myfile.txt" ->//切割的本质就是把所有的特殊符号改为\0
void commandCheak(char* commands)
{
    char* start = commands;
    char* end = commands+strlen(commands);

    while(start<end)
    {
        if(*start == '>')
        {
            *start = '\0';
            start++;
             // "ls -a >> file.log"
            if(*start == '>')
            {
                start++;
                redirType = APPEND_REDIR;//追加
            }
            else
                 // "ls -a >    file.log"
            {
                redirType = OUTPUT_REDIR;//这里应该是输出重定向因为命令的结果是打印到屏幕上的我们要他不在打印到屏幕而是打印到其他的地方

            }
			trimSpace(start);
			redirFile = start;
			break;
        }
        else if(*start == '<')
        {
			*start = '\0';
			start++;
			trimSpace(start);
			redirType = INPUT_REDIiR;
			redirFile = start;
			break;

        }
        else
        {
            start++;
        }
    }
}





int main()
{
    while(1){
    //初始化 //因为每一次重定向之后还要重定向不写的话在还是上一次的
    redirType = NONE_REDIR;
    redirFile = NULL;
    errno = 0;


    char cwd[1024]={0};
    strcpy(cwd, getenv("PWD"));
    //找到最后一个/的位置
    char* last = strrchr(cwd,'/');
    last++;//指向目录名的第一个字符
    //处理如果是根目录的情况
    if(last[0]=='\0')
        last[0] = '/';
	printf("[%s@%s %s]", getenv("USER"), "host",last);
    fflush(stdout);


    //获取一行
    char* s = fgets(line_command,sizeof(line_command)-1,stdin);
    (void*)s;//防止未使用的警告
    //因为我们在输入结束的时候会按回车导致 line_command的最后一个字符是\n我们要去掉这最后一个\n;
    line_command[strlen(line_command)-1]='\0';


    //新添重定向的模拟实现 ls-l-a > myfile.txt 把命令执行的结果重定向写到myfile.txtlm
    commandCheak(line_command);



     int i = 1;
    myargv[0] = strtok(line_command, " ");
    if(myargv[0] != NULL && strcmp(myargv[0], "ls") == 0)
    {
        myargv[i++] = (char*)"--color=auto";
    }
    
        while(myargv[i++]=strtok(NULL," "));//直到返回 NULL，表示分割结束


        if(myargv[0] != NULL && strcmp(myargv[0], "cd") == 0)
        {
            if(myargv[1] != NULL) chdir(myargv[1]);
            continue;
        }
        if(myargv[0] != NULL && myargv[1] != NULL && strcmp(myargv[0], "echo") == 0)
        {
            if(strcmp(myargv[1], "$?") == 0)
            {
                printf("%d, %d\n", lastCode, lastSig);
            }
            else
            {
                printf("%s\n", myargv[1]);
            }
            continue;
        }
    

    //进程替换
    pid_t id = fork();
    assert(id!=-1);//子进程没有创建成功
    if(id==0)
    {
		// 因为命令是子进程执行的，真正重定向的工作一定要是子进程来完成
       // 如何重定向，是父进程要给子进程提供信息的
      // 这里重定向会影响父进程吗？不会，进程具有独立性
      //
            
        switch(redirType)
        {
            case NONE_REDIR:
                break;
            case INPUT_REDIiR:
                {
                    
                int fd = open(redirFile,O_RDONLY);
                if(fd <0)
                {
                    perror("open");
                    exit(errno);
                }
                dup2(fd,0);
                }
                break;
            case OUTPUT_REDIR:
            case APPEND_REDIR:
                {
                    umask(0);
                    int flag = O_WRONLY | O_CREAT;
                    if(redirType==APPEND_REDIR)
                        flag |= O_TRUNC;
                    int fd = open(redirFile,flag,0666);
                    if(fd <0) 
                    {
                        perror("open");
                        exit(errno);
                    }
                    break;
                }
            default:
                printf("bug\n");
                break;
        }
        execvp(myargv[0],myargv); //让当前子程序程序替换为其他命令
        exit(1);

    }



    int status = 0;
    pid_t ret = waitpid(id,&status,0);
    assert(ret > 0);
    (void)ret;
    lastCode = ((status>>8) & 0xFF);
    lastSig = (status & 0x7F);
    }


    return 0;


}
